﻿#include "db_api_.h"

static int indexaudio(void)
{
	unsigned int i, j;
	int res = 0;
	unsigned int br;
	RECID3ITEM *tmp = 0;
	int album_id_offset = sizeof(ALBUMINDEX) * MAXRECORD;

	assert(MAXRECORD >= hDB->audio_count);
	if(0 == hDB->audio_count) return 0;

	tmp = (RECID3ITEM*)OAL_malloc(sizeof(RECID3ITEM) * hDB->audio_count);
	if (tmp == 0)
	{
		hDB->cb(-888, 0);
		return -1;
	}

	res += load_audio_album(tmp);
	if (res)
	{
		OAL_free(tmp);
		return -1;
	}

	{
		ALBUMINDEX idx = { "unknow", 0, 0 };idx.offset = album_id_offset;
		for (j = 0; j < hDB->audio_count; j++)
		{
			if (0 == strcmp(idx.album, tmp[j]))
			{
				int id = sizeof(AUDIORECORD) * j;
				res += f_lseek(&hDB->fdb, album_id_offset);
				res += f_write(&hDB->fdb, &id, sizeof(int), &br);
				album_id_offset += sizeof(int);
				tmp[j][0] = 0;
				idx.count++;
				hDB->cb(-555, (hDB->percent++/50)%100);
			}
		}
		res += f_lseek(&hDB->fdb, sizeof(ALBUMINDEX) * hDB->album_count);
		res += f_write(&hDB->fdb, &idx, sizeof(ALBUMINDEX), &br);
		hDB->album_count++;
	}

	for (i = 0; i < hDB->audio_count; i++)
	{
		if (tmp[i][0] != 0)
		{
			ALBUMINDEX idx = { "", 0, 0 };idx.offset = album_id_offset;
			memcpy(idx.album, tmp[i], MAXID3ITEM);
			for (j = i; j < hDB->audio_count; j++)
			{
				if (tmp[j][0])
				{
					if (0 == strcmp(idx.album, tmp[j]))
					{
						int id = sizeof(AUDIORECORD) * j;
						res += f_lseek(&hDB->fdb, album_id_offset);
						res += f_write(&hDB->fdb, &id, sizeof(int), &br);
						album_id_offset += sizeof(int);
						tmp[j][0] = 0;
						idx.count++;
						hDB->cb(-555, (hDB->percent++/50)%100);
					}
				}
			}
			res += f_lseek(&hDB->fdb, sizeof(ALBUMINDEX) * hDB->album_count);
			res += f_write(&hDB->fdb, &idx, sizeof(ALBUMINDEX), &br);
			hDB->album_count++;
		}
	}

	OAL_free(tmp);
	return res;
}

#if SORT == 1
static void get_sortid(char* album, unsigned long long id[2])
{
	int i;
	int len;
	len = strlen(album);
	id[0] = id[1] = 0;

	for(i=0; i<8 && i<len; i++)
	{
		unsigned long long c = (unsigned long long)(unsigned char)(album[i]);
		id[0] |= c<<((7-i)*8);
	}
	for(i=8; i<16 && i<len; i++)
	{
		unsigned long long c = (unsigned long long)(unsigned char)(album[i]);
		id[1] |= c<<((15-i)*8);
	}
}

static int load_sort(SORTNODE *tmp)
{
	unsigned int i;
	FIL f;
	int res = 0;
	unsigned int br;
	ALBUMINDEX rec;

	res += f_open(&f, (const char*)(F_ALBUM), (unsigned char)(FA_READ));
	if(res) return -1;

	for (i = 0; i < hDB->album_count; i++)
	{
		tmp[i].offset = (int)f_tell(&f);
		res += f_read(&f, &rec, sizeof(ALBUMINDEX), &br);
		if (FR_OK == res && sizeof(ALBUMINDEX) == br)
		{
			get_sortid(rec.album, tmp[i].id);
		}
	}

	res += f_close(&f);
	return res;
}

static int save_sort(SORTNODE *tmp)
{
	unsigned int i;
	int res = 0;
	unsigned int br;
	ALBUMINDEX rec;
	FIL f_src, f_des;

	res += f_open(&f_src, (const char*)(F_ALBUM), (unsigned char)(FA_READ));
	if(res) return -1;
	res += f_open(&f_des, (const char*)(F_SORT), (unsigned char)(FA_READ | FA_WRITE | FA_CREATE_ALWAYS));
	if(res) return -1;

	for(i=0; i<hDB->album_count; i++)
	{
		res += f_lseek(&f_src, tmp[i].offset);
		res += f_read(&f_src, &rec, sizeof(ALBUMINDEX), &br);
		res += f_write(&f_des, &rec, sizeof(ALBUMINDEX), &br);
	}
	res += f_lseek(&f_src, sizeof(ALBUMINDEX) * MAXRECORD);
	res += f_lseek(&f_des, sizeof(ALBUMINDEX) * MAXRECORD);
	for(;;)
	{
		int ofs;
		res += f_read(&f_src, &ofs, sizeof(int), &br);
		if(br == 0)
			break;
		res += f_write(&f_des, &ofs, sizeof(int), &br);
	}

	res += f_close(&f_src);
	res += f_close(&f_des);

	res += f_unlink((const char*)(F_ALBUM));
	res += f_rename((const char*)(F_SORT), (const char*)(F_ALBUM));

	return res;
}

static int sort(void)
{
	int res = 0;
	SORTNODE *tmp = 0;

	assert(MAXRECORD >= hDB->album_count);
	if(hDB->album_count < 2) return 0;

	tmp = (SORTNODE*)OAL_malloc(sizeof(SORTNODE) * hDB->album_count);
	if (tmp == 0)
	{
		hDB->cb(-888, 0);
		return -1;
	}

	res += load_sort(tmp);
	if (res == FR_OK)
	{
		_db_sort(tmp, hDB->album_count);
		res += save_sort(tmp);
	}

	OAL_free(tmp);
	return res;
}
#endif

int album_build(void)
{
	int res = 0;

	assert(hDB->mount == 1);

	if(hDB->dirty & MODULE_AUDIO)
		return -1;

	hDB->album_count = 0;
	hDB->percent = 0;

	res += f_open(&hDB->fdb, (const char*)(F_ALBUM), (unsigned char)(FA_READ | FA_WRITE));
	res += indexaudio();
	hDB->dirty &= ~MODULE_ALBUM;
	res += f_close(&hDB->fdb);
#if SORT == 1
	res += sort();
#endif
	hDB->cb(-666, 100);
	return 0;
}

int get_albums(unsigned int offset, RECID3ITEM* albums, unsigned int cnt)
{
	unsigned int i = 0;
	int res = 0;
	unsigned int br;
	res += f_open(&hDB->fdb, (const char*)(F_ALBUM), (unsigned char)(FA_READ | FA_WRITE));
	res += f_lseek(&hDB->fdb, sizeof(ALBUMINDEX) * (long long)offset);
	for (i = 0; i < cnt && i + offset < hDB->album_count; i++)
	{
		ALBUMINDEX idx;
		res += f_read(&hDB->fdb, &idx, sizeof(ALBUMINDEX), &br);
		memcpy(&albums[i], idx.album, MAXID3ITEM);
	}
	res += f_close(&hDB->fdb);
	return res ? -1 : i;
}

int get_album_(char* album, ALBUMINDEX* idx)
{
	unsigned int i = 0;
	int res = 0;
	unsigned int br;

	res += f_lseek(&hDB->fdb, 0);
	for (i = 0; i < hDB->album_count; i++)
	{
		res += f_read(&hDB->fdb, idx, sizeof(ALBUMINDEX), &br);
		if (0 == strcmp(album, idx->album))
		{
			return res;
		}
	}
	return -1;
}

int get_audio_count_of_album(RECID3ITEM album)
{
	int ret = -1;
	int res = 0;
	ALBUMINDEX idx;

	res += f_open(&hDB->fdb, (const char*)(F_ALBUM), (unsigned char)(FA_READ | FA_WRITE));
	if (res) 
	{
		return -1;
	}

	res += get_album_(album, &idx);
	if (FR_OK == res)
	{
		ret = idx.count;
	}
	res += f_close(&hDB->fdb);
	return res ? -1 : ret;
}

int get_audio_name_by_album(char* album, unsigned int offset, RECUTF8NAME* fname, unsigned int cnt)
{
	int res = 0;
	unsigned int br;
	ALBUMINDEX idx;
	int* id;

	res += f_open(&hDB->fdb, (const char*)(F_ALBUM), (unsigned char)(FA_READ | FA_WRITE));
	if (res) 
	{
		return -1;
	}

	res += get_album_(album, &idx);
	if (res)
	{
		res += f_close(&hDB->fdb);
		return -1;
	}

	if (offset >= idx.count)
	{
		res += f_close(&hDB->fdb);
		return 0;
	}
	
	if (offset + cnt > idx.count)
	{
		cnt = idx.count - offset;
	}

	id = (int*)OAL_malloc(sizeof(int) * cnt);
	if (id == 0)
	{
		hDB->cb(-888, 0);
		return -1;
	}

	res += f_lseek(&hDB->fdb, (long long)idx.offset + sizeof(int) * (long long)offset);
	res += f_read(&hDB->fdb, id, sizeof(int) * cnt, &br);
	res+= load_audio_name(id, cnt, fname);

	OAL_free(id);
	res += f_close(&hDB->fdb);
	return res ? -1 : cnt;
}

int get_audio_record_by_album(char* album, unsigned int offset, AUDIORECORD* records, unsigned int cnt)
{
	int res = 0;
	unsigned int br;
	ALBUMINDEX idx;
	int* id;

	res += f_open(&hDB->fdb, (const char*)(F_ALBUM), (unsigned char)(FA_READ | FA_WRITE));
	if (res) 
	{
		return -1;
	}

	res = get_album_(album, &idx);
	if (res)
	{
		res += f_close(&hDB->fdb);
		return -1;
	}

	if (offset >= idx.count)
	{
		res += f_close(&hDB->fdb);
		return 0;
	}

	if (offset + cnt > idx.count)
	{
		cnt = idx.count - offset;
	}

	id = (int*)OAL_malloc(sizeof(int) * cnt);
	if (id == 0)
	{
		hDB->cb(-888, 0);
		return -1;
	}

	res += f_lseek(&hDB->fdb, (long long)idx.offset + sizeof(int) * (long long)offset);
	res += f_read(&hDB->fdb, id, sizeof(int) * cnt, &br);
	res+= load_audio_records(id, cnt, records);

	OAL_free(id);
	res += f_close(&hDB->fdb);
	return res ? -1 : cnt;
}

int delete_album(char* path)
{
	hDB->album_count = 0;
	hDB->dirty |= MODULE_ALBUM;
	return 0;
}
